#!/usr/bin/env node

// Initialize memory configuration early
import { MemoryManager } from './memory-init.js';
MemoryManager.initialize();

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  McpError,
  ErrorCode,
  JSONRPCRequest,
  JSONRPCResponse,
} from '@modelcontextprotocol/sdk/types.js';
import fetch, { Response } from 'node-fetch';
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

interface ProxyConfig {
  proxy: {
    name: string;
    version: string;
    httpTarget: {
      host: string;
      port: number;
      protocol: string;
      basePath: string;
      healthPath: string;
    };
    retry: {
      maxAttempts: number;
      delayMs: number;
      backoffMultiplier: number;
    };
    timeout: {
      requestTimeoutMs: number;
      sessionTimeoutMs: number;
    };
    logging: {
      level: string;
      enableRequestLogging: boolean;
      enableErrorLogging: boolean;
    };
  };
  session: {
    autoInitialize: boolean;
    keepAlive: boolean;
    keepAliveIntervalMs: number;
  };
}

/**
 * MCP Proxy Server
 * Bridges local stdio MCP clients to HTTP MCP server
 */
class MCPProxyServer {
  private server: Server;
  private config: ProxyConfig;
  private httpBaseUrl: string;
  private sessionId: string | null = null;
  private keepAliveTimer: NodeJS.Timeout | null = null;

  constructor(configPath?: string) {
    console.error('[MCP Proxy] 初始化MCP代理服务器...');
    
    // Load configuration
    const defaultConfigPath = join(__dirname, '..', 'config', 'proxy.json');
    const actualConfigPath = configPath || defaultConfigPath;
    
    try {
      const configContent = readFileSync(actualConfigPath, 'utf-8');
      this.config = JSON.parse(configContent);
    } catch (error) {
      console.error(`[MCP Proxy] 配置文件加载失败: ${actualConfigPath}`, error);
      throw new Error(`Failed to load proxy configuration: ${error}`);
    }

    // Override with environment variables if provided
    const envHost = process.env.MCP_HTTP_HOST;
    const envPort = process.env.MCP_HTTP_PORT;
    if (envHost) this.config.proxy.httpTarget.host = envHost;
    if (envPort) this.config.proxy.httpTarget.port = parseInt(envPort);

    this.httpBaseUrl = `${this.config.proxy.httpTarget.protocol}://${this.config.proxy.httpTarget.host}:${this.config.proxy.httpTarget.port}`;
    
    console.error(`[MCP Proxy] HTTP目标服务器: ${this.httpBaseUrl}`);
    
    // Initialize MCP Server
    this.server = new Server({
      name: this.config.proxy.name,
      version: this.config.proxy.version,
    });

    this.setupHandlers();
  }

  private log(level: 'info' | 'error', message: string, ...args: any[]) {
    if (level === 'error' && this.config.proxy.logging.enableErrorLogging) {
      console.error(`[MCP Proxy ${level.toUpperCase()}] ${message}`, ...args);
    } else if (level === 'info' && this.config.proxy.logging.enableRequestLogging) {
      console.error(`[MCP Proxy ${level.toUpperCase()}] ${message}`, ...args);
    }
  }

  private async initializeHttpSession(): Promise<void> {
    if (this.sessionId) {
      this.log('info', '会话已存在，跳过初始化');
      return;
    }

    const initRequest: JSONRPCRequest = {
      jsonrpc: '2.0',
      id: 'init-' + Date.now(),
      method: 'initialize',
      params: {
        protocolVersion: '2025-03-26',
        capabilities: {},
        clientInfo: {
          name: this.config.proxy.name,
          version: this.config.proxy.version
        }
      }
    };

    try {
      const response = await this.makeHttpRequest(initRequest);
      if (response.result && response.result.sessionId) {
        this.sessionId = response.result.sessionId;
        this.log('info', `HTTP会话初始化成功: ${this.sessionId}`);
        
        // Setup keep-alive if enabled
        if (this.config.session.keepAlive) {
          this.setupKeepAlive();
        }
      } else {
        this.log('error', 'HTTP会话初始化失败：无会话ID', response);
      }
    } catch (error) {
      this.log('error', 'HTTP会话初始化失败', error);
      throw new McpError(ErrorCode.InternalError, `Failed to initialize HTTP session: ${error}`);
    }
  }

  private setupKeepAlive() {
    if (this.keepAliveTimer) {
      clearInterval(this.keepAliveTimer);
    }

    this.keepAliveTimer = setInterval(async () => {
      try {
        await this.checkHttpServerHealth();
        this.log('info', 'Keep-alive检查成功');
      } catch (error) {
        this.log('error', 'Keep-alive检查失败', error);
        // Try to reinitialize session
        this.sessionId = null;
        try {
          await this.initializeHttpSession();
        } catch (reinitError) {
          this.log('error', '会话重新初始化失败', reinitError);
        }
      }
    }, this.config.session.keepAliveIntervalMs);
  }

  private async checkHttpServerHealth(): Promise<void> {
    const healthUrl = `${this.httpBaseUrl}${this.config.proxy.httpTarget.healthPath}`;
    const response = await fetch(healthUrl, {
      timeout: this.config.proxy.timeout.requestTimeoutMs / 2
    } as any);
    
    if (!response.ok) {
      throw new Error(`Health check failed: ${response.status} ${response.statusText}`);
    }
  }

  private async makeHttpRequest(request: JSONRPCRequest, retryCount = 0): Promise<any> {
    const url = `${this.httpBaseUrl}${this.config.proxy.httpTarget.basePath}`;
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
    };

    // Add session header if we have one
    if (this.sessionId) {
      headers['Mcp-Session-Id'] = this.sessionId;
    }

    try {
      this.log('info', `发送HTTP请求: ${request.method}`, { id: request.id });
      
      const response = await fetch(url, {
        method: 'POST',
        headers,
        body: JSON.stringify(request),
        timeout: this.config.proxy.timeout.requestTimeoutMs,
      } as any);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const responseData = await response.json() as any;
      
      this.log('info', `收到HTTP响应: ${request.method}`, { id: responseData.id });
      
      return responseData;
    } catch (error) {
      this.log('error', `HTTP请求失败: ${request.method}`, error);
      
      // Retry logic
      if (retryCount < this.config.proxy.retry.maxAttempts - 1) {
        const delay = this.config.proxy.retry.delayMs * Math.pow(this.config.proxy.retry.backoffMultiplier, retryCount);
        this.log('info', `重试 ${retryCount + 1}/${this.config.proxy.retry.maxAttempts}，延迟 ${delay}ms`);
        
        await new Promise(resolve => setTimeout(resolve, delay));
        return this.makeHttpRequest(request, retryCount + 1);
      }

      throw error;
    }
  }

  private setupHandlers() {
    // List tools handler
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      // Ensure HTTP session is initialized
      if (this.config.session.autoInitialize && !this.sessionId) {
        await this.initializeHttpSession();
      }

      const request: JSONRPCRequest = {
        jsonrpc: '2.0',
        id: 'list-tools-' + Date.now(),
        method: 'tools/list',
        params: {}
      };

      try {
        const response = await this.makeHttpRequest(request);
        if (response.error) {
          throw new McpError(
            response.error.code as ErrorCode,
            response.error.message,
            response.error.data
          );
        }
        return response.result;
      } catch (error) {
        this.log('error', '列表工具请求失败', error);
        throw new McpError(
          ErrorCode.InternalError,
          `Failed to list tools via HTTP proxy: ${error}`
        );
      }
    });

    // Call tool handler
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      // Ensure HTTP session is initialized
      if (this.config.session.autoInitialize && !this.sessionId) {
        await this.initializeHttpSession();
      }

      const httpRequest: JSONRPCRequest = {
        jsonrpc: '2.0',
        id: 'call-tool-' + Date.now(),
        method: 'tools/call',
        params: request.params
      };

      try {
        const response = await this.makeHttpRequest(httpRequest);
        if (response.error) {
          throw new McpError(
            response.error.code as ErrorCode,
            response.error.message,
            response.error.data
          );
        }
        return response.result;
      } catch (error) {
        this.log('error', '调用工具请求失败', error);
        throw new McpError(
          ErrorCode.InternalError,
          `Failed to call tool via HTTP proxy: ${error}`
        );
      }
    });
  }

  async run() {
    try {
      // Check HTTP server health before starting
      await this.checkHttpServerHealth();
      this.log('info', 'HTTP服务器健康检查通过');

      // Initialize session if auto-initialize is enabled
      if (this.config.session.autoInitialize) {
        await this.initializeHttpSession();
      }

      // Start stdio transport
      const transport = new StdioServerTransport();
      await this.server.connect(transport);
      
      console.error('[MCP Proxy] 代理服务器已启动，等待stdio连接...');
      console.error(`[MCP Proxy] 代理目标: ${this.httpBaseUrl}`);
      console.error(`[MCP Proxy] 会话ID: ${this.sessionId || '未初始化'}`);
      
    } catch (error) {
      this.log('error', '启动失败', error);
      throw error;
    }
  }

  shutdown() {
    this.log('info', '关闭代理服务器...');
    
    if (this.keepAliveTimer) {
      clearInterval(this.keepAliveTimer);
      this.keepAliveTimer = null;
    }

    // Clean up HTTP session if needed
    if (this.sessionId) {
      // Note: In a real implementation, you might want to send a cleanup request
      this.log('info', `清理会话: ${this.sessionId}`);
      this.sessionId = null;
    }
  }
}

// Start server if run directly
if (import.meta.url === `file://${process.argv[1]}`) {
  const configPath = process.argv[2]; // Optional config path argument
  const proxyServer = new MCPProxyServer(configPath);
  
  proxyServer.run().catch((error) => {
    console.error('[MCP Proxy] 启动失败:', error);
    process.exit(1);
  });
  
  // Graceful shutdown
  process.on('SIGINT', () => {
    console.error('\n[MCP Proxy] 正在关闭代理服务器...');
    proxyServer.shutdown();
    process.exit(0);
  });

  process.on('SIGTERM', () => {
    console.error('\n[MCP Proxy] 收到终止信号，正在关闭...');
    proxyServer.shutdown();
    process.exit(0);
  });
}

export { MCPProxyServer };